

#include <vector>
#include <cpp/optional.hpp>
#include "catch.hpp"


TEST_CASE("optional:Test")
{
	cpp::optional<int> obj;
	cpp::optional<int> obj2;

	REQUIRE(!obj);

	obj.emplace(0);
	REQUIRE(obj);

	REQUIRE(obj.value() == 0);
	REQUIRE(*obj == obj.value());

	*obj = 100;
	REQUIRE(obj.value() == 100);
	REQUIRE(*obj == obj.value());

	obj = {};
	REQUIRE(!obj);

	obj.emplace(0);
	REQUIRE(obj);

	obj = cpp::nullopt;
	REQUIRE(obj.value_or(-100) == -100);

	obj = obj2 = 100;
	REQUIRE(obj2.value() == 100);
	REQUIRE(*obj2 == obj.value());

	cpp::optional<std::vector<int> > v1{ cpp::in_place,{ 0, 1, 2 } };
	cpp::optional<std::vector<int> > v2(v1);

	REQUIRE(v1 == v2);

	v1.emplace({ 1, 2, 3, 4 });
	v2.emplace({ 1, 2, 3, 4 });

	REQUIRE(v1 == v2);

	v1 = std::move(v2);

	REQUIRE(v2.value() == std::vector < int > {});


	{
		auto a = cpp::make_optional(10);

		auto b = cpp::make_optional< std::vector<int> >({ 1,2,3 });

	}
}


using namespace cpp;

struct NoDefault {
	NoDefault(int, int) {}
	char a, b, c;
};
static_assert(sizeof(optional<char>) == 2, "");
static_assert(sizeof(optional<int>) == 8, "");
static_assert(sizeof(optional<NoDefault>) == 4, "");

TEST_CASE("optional:NoDefault")
{
	optional<NoDefault> x;
	REQUIRE_FALSE(x);

	x.emplace(4, 5);
	REQUIRE(bool(x));
	x.reset();
	REQUIRE_FALSE(x);
}

TEST_CASE("optional:String")
{
	optional<std::string> maybeString;
	REQUIRE_FALSE(maybeString);
	maybeString = "hello";
	REQUIRE(bool(maybeString));
}


TEST_CASE("optional:Const") {
	{ // default construct
		optional<const int> opt;
		REQUIRE_FALSE(bool(opt));
		opt.emplace(4);
		REQUIRE(*opt == 4);
		opt.emplace(5);
		REQUIRE(*opt == 5);
		opt.reset();
		REQUIRE_FALSE(bool(opt));
	}
	{ // copy-constructed
		const int x = 6;
		optional<const int> opt(x);
		REQUIRE(*opt == 6);
	}
	{ // move-constructed
		const int x = 7;
		optional<const int> opt(std::move(x));
		REQUIRE(*opt == 7);
	}
	// no assignment allowed
}

TEST_CASE("Optional: Simple") {
	optional<int> opt;
	REQUIRE_FALSE(bool(opt));
	REQUIRE(42 == opt.value_or(42));
	opt = 4;
	REQUIRE(bool(opt));
	REQUIRE(4 == *opt);
	REQUIRE(4 == opt.value_or(42));
	opt = 5;
	REQUIRE(5 == *opt);
	opt.reset();
	REQUIRE_FALSE(bool(opt));
}

class MoveTester {
public:
	/* implicit */ MoveTester(const char* s) : s_(s) {}
	MoveTester(const MoveTester&) = default;
	MoveTester(MoveTester&& other) noexcept {
		s_ = std::move(other.s_);
		other.s_ = "";
	}
	MoveTester& operator=(const MoveTester&) = default;
	MoveTester& operator=(MoveTester&& other) noexcept {
		s_ = std::move(other.s_);
		other.s_ = "";
		return *this;
	}
private:
	friend bool operator==(const MoveTester& o1, const MoveTester& o2);
	std::string s_;
};

bool operator==(const MoveTester& o1, const MoveTester& o2) {
	return o1.s_ == o2.s_;
}



TEST_CASE("Optional: value_or_rvalue_arg") {
	optional<MoveTester> opt;
	MoveTester dflt = "hello";
	REQUIRE("hello" == opt.value_or(dflt));
	REQUIRE("hello" == dflt);
	REQUIRE("hello" == opt.value_or(std::move(dflt)));
	REQUIRE("" == dflt);
	REQUIRE("world" == opt.value_or("world"));

	dflt = "hello";
	// Make sure that the const overload works on const objects
	const auto& optc = opt;
	REQUIRE("hello" == optc.value_or(dflt));
	REQUIRE("hello" == dflt);
	REQUIRE("hello" == optc.value_or(std::move(dflt)));
	REQUIRE("" == dflt);
	REQUIRE("world" == optc.value_or("world"));

	dflt = "hello";
	opt = "meow";
	REQUIRE("meow" == opt.value_or(dflt));
	REQUIRE("hello" == dflt);
	REQUIRE("meow" == opt.value_or(std::move(dflt)));
	REQUIRE("hello" == dflt);  // only moved if used
}


TEST_CASE("Optional, value_or_noncopyable")
{
	optional<std::unique_ptr<int>> opt;
	std::unique_ptr<int> dflt(new int(42));
	REQUIRE(42 == *std::move(opt).value_or(std::move(dflt)));
}

struct ExpectingDeleter {
	explicit ExpectingDeleter(int expected) : expected(expected) { }
	int expected;
	void operator()(const int* ptr) {
		REQUIRE(*ptr == expected);
		delete ptr;
	}
};



TEST_CASE("Optional, value_move") {
	auto ptr = optional<std::unique_ptr<int, ExpectingDeleter>>(
	{ new int(42), ExpectingDeleter{ 1337 } }).value();
	*ptr = 1337;
}

TEST_CASE("Optional, dereference_move") {
	auto ptr = *optional<std::unique_ptr<int, ExpectingDeleter>>(
	{ new int(42), ExpectingDeleter{ 1337 } });
	*ptr = 1337;
}


TEST_CASE("Optional, EmptyConstruct") {
	optional<int> opt;
	REQUIRE_FALSE(bool(opt));
	optional<int> test1(opt);
	REQUIRE_FALSE(bool(test1));
	optional<int> test2(std::move(opt));
	REQUIRE_FALSE(bool(test2));
}

TEST_CASE("Optional, Unique") {
	using namespace std;
	optional<std::unique_ptr<int>> opt;

	opt.reset();
	REQUIRE_FALSE(bool(opt));
	// empty->emplaced
	opt.emplace(new int(5));
	REQUIRE(bool(opt));
	REQUIRE(5 == **opt);

	opt.reset();
	// empty->moved
	opt = unique_ptr<int>(new int(6));
	REQUIRE(6 == **opt);
	// full->moved
	opt = unique_ptr<int>(new int(7));
	REQUIRE(7 == **opt);

	// move it out by move construct
	optional<unique_ptr<int>> moved(std::move(opt));
	REQUIRE(bool(moved));
	REQUIRE_FALSE(bool(*opt));
	REQUIRE(7 == **moved);

	REQUIRE(bool(moved));
	opt = std::move(moved); // move it back by move assign
	REQUIRE_FALSE(bool(*moved));
	REQUIRE(bool(opt));
	REQUIRE(7 == **opt);
}


TEST_CASE("Optional, Shared") {
	using namespace std;
	shared_ptr<int> ptr;
	optional< shared_ptr<int> > opt;
	REQUIRE_FALSE(bool(opt));
	// empty->emplaced
	opt.emplace(new int(5));
	REQUIRE(bool(opt));
	ptr = opt.value();
	REQUIRE(ptr.get() == opt->get());
	REQUIRE(2 == ptr.use_count());
	opt.reset();
	REQUIRE(1 == ptr.use_count());
	// full->copied
	opt = ptr;
	REQUIRE(2 == ptr.use_count());
	REQUIRE(ptr.get() == opt->get());
	opt.reset();
	REQUIRE(1 == ptr.use_count());
	// full->moved
	opt = std::move(ptr);
	REQUIRE(1 == opt->use_count());
	REQUIRE(nullptr == ptr.get());
	{
		optional<shared_ptr<int>> copied(opt);
		REQUIRE(2 == opt->use_count());
		optional<shared_ptr<int>> moved(std::move(opt));
		REQUIRE(2 == moved->use_count());
		moved.emplace(new int(6));
		REQUIRE(1 == moved->use_count());
		copied = moved;
		REQUIRE(2 == moved->use_count());
	}
}


TEST_CASE("Optional, Order") {
	std::vector<optional<int>> vect{
		{ nullopt },
		{ 3 },
		{ 1 },
		{ nullopt },
		{ 2 },
	};
	std::vector<optional<int>> expected{
		{ nullopt },
		{ nullopt },
		{ 1 },
		{ 2 },
		{ 3 },
	};
	std::sort(vect.begin(), vect.end());
	REQUIRE(vect == expected);
}

TEST_CASE("Optional, Swap") {
	optional<std::string> a;
	optional<std::string> b;

	swap(a, b);
	REQUIRE_FALSE(a.has_value());
	REQUIRE_FALSE(b.has_value());

	a = "hello";
	REQUIRE(a.has_value());
	REQUIRE_FALSE(b.has_value());
	REQUIRE("hello" == a.value());

	swap(a, b);
	REQUIRE_FALSE(a.has_value());
	REQUIRE(b.has_value());
	REQUIRE("hello" == b.value());

	a = "bye";
	REQUIRE(a.has_value());
	REQUIRE("bye" == a.value());

	swap(a, b);
}



TEST_CASE("Optional, Comparisons") {
	optional<int> o_;
	optional<int> o1(1);
	optional<int> o2(2);

	REQUIRE(o_ <= (o_));
	REQUIRE(o_ == (o_));
	REQUIRE(o_ >= (o_));

	REQUIRE(o1 < o2);
	REQUIRE(o1 <= o2);
	REQUIRE(o1 <= (o1));
	REQUIRE(o1 == (o1));
	REQUIRE(o1 != o2);
	REQUIRE(o1 >= (o1));
	REQUIRE(o2 >= o1);
	REQUIRE(o2 > o1);

	REQUIRE_FALSE(o2 < o1);
	REQUIRE_FALSE(o2 <= o1);
	REQUIRE_FALSE(o2 <= o1);
	REQUIRE_FALSE(o2 == o1);
	REQUIRE_FALSE(o1 != (o1));
	REQUIRE_FALSE(o1 >= o2);
	REQUIRE_FALSE(o1 >= o2);
	REQUIRE_FALSE(o1 > o2);

	REQUIRE(1 < o2);
	REQUIRE(1 <= o2);
	REQUIRE(1 <= o1);
	REQUIRE(1 == o1);
	REQUIRE(2 != o1);
	REQUIRE(1 >= o1);
	REQUIRE(2 >= o1);
	REQUIRE(2 > o1);

	REQUIRE_FALSE(o2 < 1);
	REQUIRE_FALSE(o2 <= 1);
	REQUIRE_FALSE(o2 <= 1);
	REQUIRE_FALSE(o2 == 1);
	REQUIRE_FALSE(o2 != 2);
	REQUIRE_FALSE(o1 >= 2);
	REQUIRE_FALSE(o1 >= 2);
	REQUIRE_FALSE(o1 > 2);

}


TEST_CASE("Optional, Conversions")
{
	optional<bool> mbool;
	optional<short> mshort;
	optional<char*> mstr;
	optional<int> mint;

	//These don't compile
	//bool b = mbool;
	//short s = mshort;
	//char* c = mstr;
	//int x = mint;
	//char* c(mstr);
	//short s(mshort);
	//int x(mint);

	// intended explicit operator bool, for if (opt).
	bool b(mbool);
	REQUIRE_FALSE(b);

	// Truthy tests work and are not ambiguous
	if(mbool && mshort && mstr && mint) { // only checks not-empty
		if(*mbool && *mshort && *mstr && *mint) { // only checks value
			;
		}
	}

	mbool = false;
	REQUIRE(bool(mbool));
	REQUIRE_FALSE(*mbool);

	mbool = true;
	REQUIRE(bool(mbool));
	REQUIRE(*mbool);

	mbool = nullopt;
	REQUIRE_FALSE(bool(mbool));

	// No conversion allowed; does not compile
	// EXPECT_TRUE(mbool == false);
}


TEST_CASE("Optional, MakeOptional") {
	// const L-value version
	const std::string s("abc");
	auto optStr = make_optional(s);
	REQUIRE(optStr.has_value());
	REQUIRE(*optStr == "abc");
	*optStr = "cde";
	REQUIRE(s == "abc");
	REQUIRE(*optStr == "cde");

	// L-value version
	std::string s2("abc");
	auto optStr2 = make_optional(s2);
	REQUIRE(optStr2.has_value());
	REQUIRE(*optStr2 == "abc");
	*optStr2 = "cde";
	// it's vital to check that s2 wasn't clobbered
	REQUIRE(s2 == "abc");

	// L-value reference version
	std::string& s3(s2);
	auto optStr3 = make_optional(s3);
	REQUIRE(optStr3.has_value());
	REQUIRE(*optStr3 == "abc");
	*optStr3 = "cde";
	REQUIRE(s3 == "abc");

	// R-value ref version
	std::unique_ptr<int> pInt(new int(3));
	auto optIntPtr = make_optional(std::move(pInt));
	REQUIRE(pInt.get() == nullptr);
	REQUIRE(optIntPtr.has_value());
	REQUIRE(**optIntPtr == 3);
}


TEST_CASE("Optional, SelfAssignment") {

	optional<int> a = 42;
	a = a;
	REQUIRE((a.has_value() && a.value() == 42));

	optional<int> b = 23333333;
	b = std::move(b);
	REQUIRE((b.has_value() && b.value() == 23333333));
}


class ContainsOptional {
public:
	ContainsOptional() { }
	explicit ContainsOptional(int x) : opt_(x) { }
	bool hasValue() const { return opt_.has_value(); }
	int value() const { return opt_.value(); }

	ContainsOptional(const ContainsOptional &other) = default;
	ContainsOptional& operator=(const ContainsOptional &other) = default;
	ContainsOptional(ContainsOptional &&other) = default;
	ContainsOptional& operator=(ContainsOptional &&other) = default;

private:
	optional<int> opt_;
};


TEST_CASE("Optional, AssignmentContained") {
	{
		ContainsOptional source(5), target;
		target = source;
		REQUIRE(target.hasValue());
		REQUIRE(5 == target.value());
	}

	{
		ContainsOptional source(5), target;
		target = std::move(source);
		REQUIRE(target.hasValue());
		REQUIRE(5 == target.value());
		REQUIRE(source.hasValue());
	}

	{
		ContainsOptional opt_uninit, target(10);
		target = opt_uninit;
		REQUIRE_FALSE(target.hasValue());
	}
}

TEST_CASE("Optional, Exceptions") {
	optional<int> empty;
	REQUIRE_THROWS_AS(empty.value(), bad_optional_access);
}

TEST_CASE("Optional, NoThrowDefaultConstructible") {
	REQUIRE(std::is_nothrow_default_constructible<optional<bool>>::value);
}


struct NoDestructor {};

struct WithDestructor {
	~WithDestructor();
};

TEST_CASE("Optional, TriviallyDestructible") {
	// These could all be static_asserts but EXPECT_* give much nicer output on
	// failure.
	REQUIRE(std::is_trivially_destructible<optional<NoDestructor>>::value);
	REQUIRE(std::is_trivially_destructible<optional<int>>::value);
	REQUIRE_FALSE(std::is_trivially_destructible<optional<WithDestructor>>::value);
}